package io.gitee.zhangbinhub.admin.oauth.conf

import io.gitee.zhangbinhub.acp.boot.interfaces.LogAdapter
import io.gitee.zhangbinhub.acp.cloud.conf.AcpCloudOauthConfiguration
import io.gitee.zhangbinhub.acp.cloud.constant.AcpCloudConstant
import io.gitee.zhangbinhub.acp.cloud.constant.RestPrefix
import io.gitee.zhangbinhub.acp.core.CommonTools
import io.gitee.zhangbinhub.admin.common.tools.TokenTools
import io.gitee.zhangbinhub.admin.oauth.authentication.OauthUserPasswordAuthenticationConverter
import io.gitee.zhangbinhub.admin.oauth.authentication.OauthUserPasswordAuthenticationProvider
import io.gitee.zhangbinhub.admin.oauth.component.AuthPasswordEncrypt
import io.gitee.zhangbinhub.admin.oauth.component.AuthTokenService
import io.gitee.zhangbinhub.admin.oauth.component.AuthUserService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.web.ServerProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.annotation.Order
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer
import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2TokenEndpointConfigurer
import org.springframework.security.oauth2.core.OAuth2Token
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationProvider
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings
import org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator
import org.springframework.security.oauth2.server.authorization.token.OAuth2AccessTokenGenerator
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator
import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientCredentialsAuthenticationConverter
import org.springframework.security.web.AuthenticationEntryPoint
import org.springframework.security.web.SecurityFilterChain
import org.springframework.security.web.access.AccessDeniedHandler
import java.util.*

/**
 * @author zhangbin by 11/04/2018 14:34
 * @since JDK 11
 */
@Configuration
class AuthorizationServerConfiguration @Autowired constructor(
    serverProperties: ServerProperties,
    private val logAdapter: LogAdapter,
    private val tokenTools: TokenTools,
    private val authTokenService: AuthTokenService,
    private val authPasswordEncrypt: AuthPasswordEncrypt,
    private val entryPointMap: Map<String, AuthenticationEntryPoint>,
    private val accessDeniedHandlerMap: Map<String, AccessDeniedHandler>,
    private val acpCloudOauthConfiguration: AcpCloudOauthConfiguration,
    private val authUserService: AuthUserService
) {
    private val contextPath: String =
        if (CommonTools.isNullStr(serverProperties.servlet.contextPath)) "" else serverProperties.servlet.contextPath

    @Bean
    @Order(AcpCloudConstant.resourceServerConfiguration - 1)
    @Throws(Exception::class)
    fun authorizationServerSecurityFilterChain(httpSecurity: HttpSecurity): SecurityFilterChain? {
        val authorizationServerConfigurer = OAuth2AuthorizationServerConfigurer<HttpSecurity>()
        val endpointsMatcher = authorizationServerConfigurer.endpointsMatcher
        // 配置 endpoint 策略
        httpSecurity.csrf().disable()
            .authorizeRequests { authorizeRequests ->
                authorizeRequests.requestMatchers(endpointsMatcher).permitAll()
                    .antMatchers("$contextPath${RestPrefix.Open}/**").permitAll()
            }.apply(authorizationServerConfigurer)
        // 自定义token端点配置
        val tokenGenerator: OAuth2TokenGenerator<OAuth2Token> =
            DelegatingOAuth2TokenGenerator(OAuth2AccessTokenGenerator())
        authorizationServerConfigurer.tokenEndpoint { tokenEndpoint: OAuth2TokenEndpointConfigurer ->
            tokenEndpoint.accessTokenRequestConverter(
                DelegatingAuthenticationConverter(
                    listOf(
                        OAuth2ClientCredentialsAuthenticationConverter(),
                        OauthUserPasswordAuthenticationConverter()
                    )
                )
            )
            tokenEndpoint.authenticationProvider(
                OAuth2ClientCredentialsAuthenticationProvider(
                    authTokenService, tokenGenerator
                )
            )
            tokenEndpoint.authenticationProvider(
                OauthUserPasswordAuthenticationProvider(
                    logAdapter,
                    tokenTools,
                    authUserService,
                    authPasswordEncrypt,
                    tokenGenerator,
                    authTokenService
                )
            )
        }.tokenGenerator(tokenGenerator)
        // 关闭session
        httpSecurity.sessionManagement().disable()
        httpSecurity.oauth2ResourceServer().opaqueToken()
        // 自定义 token 异常处理
        if (entryPointMap.isNotEmpty()) {
            if (entryPointMap.size > 1) {
                if (!CommonTools.isNullStr(acpCloudOauthConfiguration.authExceptionEntryPoint)) {
                    httpSecurity.oauth2ResourceServer()
                        .authenticationEntryPoint(entryPointMap[acpCloudOauthConfiguration.authExceptionEntryPoint])
                } else {
                    logAdapter.warn("Find more than one authenticationEntryPoint, please specify explicitly in the configuration 'acp.cloud.oauth.auth-exception-entry-point'")
                }
            } else {
                httpSecurity.oauth2ResourceServer()
                    .authenticationEntryPoint(entryPointMap.entries.iterator().next().value)
            }
        }
        // 自定义权限异常处理
        if (accessDeniedHandlerMap.isNotEmpty()) {
            if (accessDeniedHandlerMap.size > 1) {
                if (!CommonTools.isNullStr(acpCloudOauthConfiguration.accessDeniedHandler)) {
                    httpSecurity.oauth2ResourceServer()
                        .accessDeniedHandler(accessDeniedHandlerMap[acpCloudOauthConfiguration.accessDeniedHandler])
                } else {
                    logAdapter.warn("Find more than one accessDeniedHandler, please specify explicitly in the configuration 'acp.cloud.oauth.access-denied-handler'")
                }
            } else {
                httpSecurity.oauth2ResourceServer()
                    .accessDeniedHandler(accessDeniedHandlerMap.entries.iterator().next().value)
            }
        }
        return httpSecurity.build()
    }

    /**
     * 设置endpoint的url
     *
     * @return ProviderSettings
     */
    @Bean
    fun providerSettings(): ProviderSettings = ProviderSettings.builder()
        .authorizationEndpoint("/oauth/authorize")
        .tokenEndpoint("/oauth/token")
        .jwkSetEndpoint("/oauth/jwks")
        .tokenRevocationEndpoint("/oauth/revoke")
        .tokenIntrospectionEndpoint("/inner/oauth/introspect")
        .build()
}
